﻿#include "ast_to_json.hpp"
#include "cpp-gen/CommonLexer.h"
#include "cpp-gen/FormalParser.h"
#include "cpp-gen/LooseParser.h"
#include "estree_visitor.hpp"
#include "bail_error_strategy.h"
#include "abstract_parser.hpp"
#include "syntax_error.h"
#include "macros.h"
#include "utils.h"
#include "cyclone_type_visitor.h"
#include "configurable_parser.hpp"
#include "configurable_visitor.hpp"

namespace cyclone::parser
{
    DECLARE_STATIC_STRING_WITH_DEFAULT(input);
    DECLARE_STATIC_STRING_WITH_DEFAULT(output);
    DECLARE_STATIC_STRING_WITH_DEFAULT(program);
    DECLARE_STATIC_STRING_WITH_DEFAULT(vardecl);
    DECLARE_STATIC_STRING_WITH_DEFAULT(body);
    DECLARE_STATIC_STRING_WITH_DEFAULT(type);

    using namespace cyclone;
    using FormalParser = AbstractParser<CommonLexer, FormalParser, ParseMode::Formal>;
    using LooseParser = AbstractParser<CommonLexer, LooseParser, ParseMode::Loose>;
    using VisitorPtr = std::shared_ptr<JsonVisitor>;

    static const std::list<std::string> kSpecializedRules = {
        input, output, vardecl, type};

    static auto formatVaDeclarationInput(const std::string &input)
    {
        return std::string("let " + input + ";");
    }

    static auto formatParserInput(const ParseOption &option)
    {
        return vardecl == option.type ? formatVaDeclarationInput(option.valueRaw) : option.valueRaw;
    }

    template <ParseMode Mode, typename Parser>
    static Json genericParse(Parser &&parser, const ParseOption &option, EstreeVisitor &&estreeVisitor)
    {
        parser.init(formatParserInput(option));
        parser.enableParensInType(option.allowParensInType);
        parser.enableUndefinedInType(option.allowUndefinedInType);
        estreeVisitor.enableParens(option.preserveParens);
        estreeVisitor.enableLocation(option.locations);
        // What's wrong with the complier if skip??
        auto &visitor = static_cast<JsonVisitor &>(estreeVisitor);
        const auto bindingTypeToRule = [&]()
        {
            if (input == option.type)
                return visitor.visit(&parser.template applyRule<&FormalParser::input>());
            else if (output == option.type)
                return visitor.visit(&parser.template applyRule<&FormalParser::output>());
            else if (vardecl == option.type)
                return visitor.visit(&parser.template applyRule<&FormalParser::vardecl>());
            else if (type == option.type)
                return visitor.visit(&parser.template applyRule<&FormalParser::typeAnnot>());
            else
                return visitor.visit(&parser.template applyRule<&FormalParser::program>());
        };
        if constexpr (ParseMode::Formal == Mode)
        {
            try
            {
                return bindingTypeToRule();
            }
            catch (const SyntaxError &e)
            {
                // WebAssembly currently does not support throwing exception from C++ code
                // Throwing exceptions from an event handler is not supported in Qt
                return e;
            }
        }
        return bindingTypeToRule();
    }

    AstToJson::AstToJson()
    {
    }

    AstToJson::~AstToJson()
    {
    }

    bool AstToJson::isSyntaxError(const Json &result)
    {
        return !result["message"].is_null();
    }

    Json AstToJson::parse(const ParseOption &option)
    {
        return genericParse<ParseMode::Formal>(FormalParser(), option, EstreeVisitor());
    }

    Json AstToJson::parseLoose(const ParseOption &option)
    {
        return genericParse<ParseMode::Loose>(LooseParser(), option, EstreeVisitor());
    }

    Json AstToJson::parse(const std::string &valueRaw)
    {
        return parse(ParseOption{false, false, program, valueRaw});
    }

    Json AstToJson::parseLoose(const std::string &valueRaw)
    {
        return parseLoose(ParseOption{false, false, program, valueRaw});
    }

    Json AstToJson::parseType(const std::string &annot, const ParseTypeOption &option)
    {
        return genericParse<ParseMode::Formal>(FormalParser(), {option.allowParensInType, option.allowUndefinedInType, type, annot}, CycloneTypeVisitor());
    }

    Json AstToJson::parseTypeLoose(const std::string &annot, const ParseTypeOption &option)
    {
        return genericParse<ParseMode::Loose>(LooseParser(), {option.allowParensInType, option.allowUndefinedInType, type, annot}, CycloneTypeVisitor());
    }

    std::list<std::string> AstToJson::getSpecializedRuleNames()
    {
        return kSpecializedRules;
    }

}
